package taskhandler

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/opensourceways/server-common-lib/utils"

	"cvevulner/common"
	"cvevulner/util"
)

const (
	keyOfDate  = "key_of_date"
	retryTimes = 3
	splitLen   = 2
)

var mutex sync.Mutex
var assignerOfOpeneulerRepoCache map[string]string

func GetOautToken(gt GiteeToken) {
	url := "https://gitee.com/oauth/token"
	var req util.RequestInfo
	req.URL = url
	req.Data = make(map[string]string)
	req.Data["grant_type"] = gt.GrantType
	req.Data["username"] = gt.UserName
	req.Data["password"] = gt.Password
	req.Data["client_id"] = gt.ClientID
	req.Data["client_secret"] = gt.ClientSecret
	req.Data["scope"] = gt.Scope
	resp, err := util.PostURLEncoded(req)
	if err != nil {
		logs.Error("Failed to obtain token,url: ", url,
			",Request parameter: ", gt, ", err:", err)
		return
	}
	var respDict map[string]interface{}
	err = json.Unmarshal(resp, &respDict)
	if err != nil {
		logs.Error(err)
		return
	}
	if _, ok := respDict["access_token"]; !ok {
		logs.Error("Failed to obtain token, err: ", ok, ", url: ", url)
		return
	}
	GitToken := respDict["access_token"].(string)
	os.Setenv("GITEE_TOKEN", GitToken)
}

func GetCollaboratorInfo(accessToken string, owner string, path string) (string, error) {
	if accessToken != "" && owner != "" && path != "" {
		url := "https://gitee.com/api/v5/repos/" + owner + "/" + path + "/collaborators?access_token=" + accessToken
		collabor, err := util.HTTPGet(url)
		if err == nil && collabor != nil {
			for _, value := range collabor {
				if _, ok := value["id"]; !ok {
					logs.Error("collaborators, err: ", ok, "url: ", url)
					continue
				}
				assignee := ""
				for mapKey, mapValue := range value {
					if mapKey == "login" {
						assignee = mapValue.(string)
					}
				}
				if assignee != "" {
					return assignee, nil
				}
			}
		} else {
			logs.Error("Failed to obtain repo owner, owner:", owner, ",path:", path, ",err: ", err)
			return "", err
		}
	}
	return "", errors.New("参数错误")
}

func GetAssignerOfOpeneuler(repo string) string {
	mutex.Lock()
	defer mutex.Unlock()
	today := time.Now().Format("20060102")

	if len(assignerOfOpeneulerRepoCache) == 0 {
		InitAssignerCache(today)
	}

	date, ok := assignerOfOpeneulerRepoCache[keyOfDate]
	if !ok || date != today {
		InitAssignerCache(today)
	}

	var ret string
	assigner, ok := assignerOfOpeneulerRepoCache[repo]

	if ok {
		ret = assigner
	}

	return ret
}

// InitAssignerCache initializes the assigner cache with today's date.
func InitAssignerCache(today string) {
	assignerOfOpeneulerRepoCache = make(map[string]string)
	assignerOfOpeneulerRepoCache[keyOfDate] = today

	cli := utils.NewHttpClient(retryTimes)
	for _, sig := range getSig() {
		url := fmt.Sprintf("https://www.openeuler.org/api-dsapi/query/sig/repo/committers?community=openeuler&sig=%s", sig)

		request, err := http.NewRequest(http.MethodGet, url, nil)
		if err != nil {
			logs.Error("new request of sig %s err: %s", sig, err.Error())
			continue
		}
		r, _, err := cli.Download(request)
		if err != nil {
			logs.Error("get assigner of sig %s err: %s", sig, err.Error())
			continue
		}

		var res ResCommitter
		if err = json.Unmarshal(r, &res); err != nil {
			logs.Error("unmarshal of sig %s err: %s", sig, err.Error())
			continue
		}

		var sigAssigner string
		if len(res.Data.Maintainers) > 0 {
			sigAssigner = res.Data.Maintainers[0]
		}

		for _, v := range res.Data.CommitterDetails {
			if !strings.Contains(v.Repo, "src-openeuler") {
				continue
			}

			var assigner string
			if len(v.GiteeId) > 0 {
				assigner = v.GiteeId[0]
			} else {
				assigner = sigAssigner
			}

			split := strings.Split(v.Repo, "/")
			if len(split) < splitLen {
				continue
			}

			assignerOfOpeneulerRepoCache[split[1]] = assigner
		}
	}
}

type ResContent struct {
	Type string `json:"type"`
	Name string `json:"name"`
}

type ResCommitter struct {
	Data struct {
		Maintainers      []string `json:"maintainers"`
		CommitterDetails []struct {
			GiteeId []string `json:"gitee_id"`
			Repo    string   `json:"repo"`
		} `json:"committerDetails"`
	} `json:"data"`
}

func getSig() []string {
	url := fmt.Sprintf("%s/api/v5/repos/openeuler/community/contents/sig?access_token=%s",
		beego.AppConfig.String("gitee::host"),
		beego.AppConfig.String("gitee::git_token"),
	)
	request, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		logs.Error("new request of sig url error: %s ", err.Error())

		return nil
	}

	cli := utils.NewHttpClient(3)
	var res []ResContent
	r, _, err := cli.Download(request)
	if err != nil {
		logs.Error("get sig of openeuler error: %s", err.Error())

		return nil
	}

	if err = json.Unmarshal(r, &res); err != nil {
		logs.Error("unmarshal sig error: %s", err.Error())

		return nil
	}

	var sig []string
	for _, v := range res {
		if v.Type == "dir" {
			sig = append(sig, v.Name)
		}
	}

	return sig
}

func GetBranchesInfo(accessToken, owner, path string, organizationID int8) ([]string, error) {
	BrandVerSlice := make([]string, 0)
	switch organizationID {
	case util.OpenLookeng:
		if openlookengVersion := beego.AppConfig.String("openlookeng::openlookeng_version"); len(openlookengVersion) > 0 {
			BrandVerSlice = strings.Split(openlookengVersion, ",")
		}
	case util.MindSpore:
		if mindsporeVersion := beego.AppConfig.String("mindspore::mindspore_version"); len(mindsporeVersion) > 0 {
			BrandVerSlice = strings.Split(mindsporeVersion, ",")
		}
	case util.OpenGauss:
		if gaussVersion := beego.AppConfig.String("opengauss::gauss_version"); len(gaussVersion) > 0 {
			BrandVerSlice = strings.Split(gaussVersion, ",")
		}
		path = beego.AppConfig.String("opengauss::openGauss-server")
	default:
		if eulerVersion := beego.AppConfig.String("cve::affected_branchs"); len(eulerVersion) > 0 {
			BrandVerSlice = strings.Split(eulerVersion, ",")
		}
	}
	branchNameList := []string{}
	if accessToken != "" && owner != "" && path != "" {
		url := "https://gitee.com/api/v5/repos/" + owner + "/" + path + "/branches?access_token=" + accessToken
		branch, err := util.HTTPGet(url)
		if err == nil && branch != nil {
			for _, value := range branch {
				if _, ok := value["name"]; !ok {
					logs.Error("branches, err: ", ok, "url: ", url)
					continue
				}
				mapValue := value["name"].(string)
				if mapValue != "" && len(mapValue) > 1 {
					switch organizationID {
					case util.OpenGauss, util.OpenLookeng:
						if !common.IsContainsLetters(mapValue) {
							branchNameList = append(branchNameList, mapValue)
						}
					case util.MindSpore:
						if common.IsContainSpecialChar(mapValue, 1) {
							branchNameList = append(branchNameList, mapValue)
						}
					default:
						for _, gv := range BrandVerSlice {
							if gv == mapValue {
								branchNameList = append(branchNameList, mapValue)
							}
						}
					}
				}
			}
		} else {
			logs.Error("Failed to get branch name, "+
				"err: ", err, ",owner: ", owner, ",path: ", path)
			if organizationID != util.Openeuler {
				logs.Info("1.Obtained branch information: ", BrandVerSlice)
				return BrandVerSlice, err
			}
			logs.Info("2.Obtained branch information: ", branchNameList)
			return branchNameList, err
		}
	}

	if len(branchNameList) > 0 {
		sort.Strings(branchNameList)
	}

	if organizationID == util.OpenGauss {
		if len(branchNameList) > 2 {
			branchNameList = branchNameList[len(branchNameList)-2:]
		}
	} else if organizationID == util.MindSpore {
		if len(branchNameList) > 4 {
			branchNameList = branchNameList[len(branchNameList)-4:]
		}
	} else if organizationID == util.OpenLookeng {
		if len(branchNameList) > 1 {
			branchNameList = branchNameList[len(branchNameList)-1:]
		}
	}

	if len(BrandVerSlice) > 0 {
		branchNameList = append(branchNameList, BrandVerSlice...)
	}

	branchNameList = common.RemoveDupString(branchNameList)

	return branchNameList, nil
}

func GetTagsInfo(accessToken, owner, path string, organizationID int8) ([]string, error) {
	var tagsList []string
	if accessToken != "" && owner != "" && path != "" {
		url := "https://gitee.com/api/v5/repos/" + owner + "/" + path + "/tags?access_token=" + accessToken
		branch, err := util.HTTPGet(url)
		if err == nil && branch != nil {
			for _, value := range branch {
				if _, ok := value["name"]; !ok {
					logs.Error("tagsInfo, err: ", ok, "url: ", url)
					continue
				}
				mapValue := value["name"].(string)
				if mapValue != "" && len(mapValue) > 1 {
					if organizationID == util.OpenLookeng {
						if !common.IsContainsLetters(mapValue) {
							tagsList = append(tagsList, mapValue)
						}
					} else {
						if common.IsContainSpecialChar(mapValue, 2) {
							tagsList = append(tagsList, mapValue)
						}
					}
				}
			}
		} else {
			logs.Error("Failed to get tags name, err: ", err, ",owner: ", owner, ",path: ", path)
			return tagsList, err
		}
	}

	if len(tagsList) > 0 {
		sort.Strings(tagsList)
	}

	if organizationID == util.OpenLookeng {
		if len(tagsList) > 1 {
			tagsList = tagsList[len(tagsList)-1:]
		}
	}

	return tagsList, nil
}
